package com.et.mserver;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// receive message from client

public class PrefixedXMLStreamDecoder extends CumulativeProtocolDecoder {

	@Override
	protected boolean doDecode(final IoSession session, final IoBuffer in, final ProtocolDecoderOutput out) throws Exception {

		ReceivedBuffer receivedBuf = (ReceivedBuffer) session.getAttribute(ReceivedBuffer.MSGBUFFER_ATTRIBUTE_KEY);

		final MContext mailContext = (MContext) session.getAttribute(MContext.SESSION_STATUS_KEY);

		if (receivedBuf == null) {
			receivedBuf = new ReceivedBuffer(MServerConstants.DEFAULT_PREFIX_LENGTH);
			session.setAttribute(ReceivedBuffer.MSGBUFFER_ATTRIBUTE_KEY, receivedBuf);
		}

		if (receivedBuf.isNewMessage()) {
			if ((mailContext != null) && (mailContext.isBinaryReceivingState())) {

				if (logger.isTraceEnabled()) {

					logger.trace(Utility.logHeadOfSession(session) + "new a binary message");
				}
				receivedBuf.setReadNormalMsg(false);
			} else {
				if (logger.isTraceEnabled()) {
					logger.trace(Utility.logHeadOfSession(session) + "new a text message");
				}

				receivedBuf.setReadNormalMsg(true);
			}
		} else {
			if (receivedBuf.getReadNormalMsg()) {

				if (logger.isTraceEnabled()) {
					logger.trace(Utility.logHeadOfSession(session) + "continue  a text message");
				}

			} else {
				if (logger.isTraceEnabled()) {
					logger.trace(Utility.logHeadOfSession(session) + "continue a binary message");
				}
			}
		}

		receivedBuf.append(in);

		if (!receivedBuf.isFull()) {
			return false;
		}

		try {
			// it is a xml message or a binary data
			if ((mailContext != null) && (mailContext.isBinaryReceivingState())) {
				final BinaryMessageSegment seg = receivedBuf.getAsBinaryMessage();
				if (seg == null) {
					return true;
				} else {
					out.write(seg);
				}
			} else {

				final String message = new String(receivedBuf.getBuffer(), MServerConstants.DEFAULT_CHARSET_NAME);

				try {
					// 绝大多数消息都是XML消息, 先以XML格式尝试解析效率较高
					final Element elem = DocumentHelper.parseText(message).getRootElement();

					if (logger.isDebugEnabled()) {
						logger.debug(Utility.logHeadOfSession(session) + "get a xml elment: " + elem.asXML());
					}

					out.write(elem);  // XML格式消息
				} catch (Exception e) {

					if (logger.isInfoEnabled()) {
						logger.info(Utility.logHeadOfSession(session) + "get a non-xml message : " + message);
					}

					out.write(message);  // 字符串格式的消息, 只有打开或关闭流时使用
				}
			}

			return true;
		} catch (Exception t) {

			if (logger.isInfoEnabled()) {
				logger.info(Utility.logHeadOfSession(session) + "decode data exception: ", t);
			}

			throw t;
		} finally {
			receivedBuf.clear();
		}
	}

	private static Logger logger = LoggerFactory.getLogger(PrefixedXMLStreamDecoder.class);
}
